/*
 * Clase Sintaxis
 * Descripción: Recibe codigo C y lo devuleve con errores sintacticos
 *
 */

package Services;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 *
 * @author Oswaldo.C
 */
public class Sintaxis {

    //var

    //implementados
    private final static int ERROR_PUNTO_Y_COMA = 1; //eliminar ; del codigo
    private final static int ERROR_OPERADOR_IGUAL = 2; //cambiar == por =
    private final static int ERROR_OPERADOR_LOGICO = 3; // cambiar != por =!, <= por =>
    private final static int ERROR_MAIN = 4; //cambiar main por Main
    private final static int ERROR_COMILLAS = 5; //quitar unas comillas del codigo
    private final static int ERROR_PRINTF = 6; //sustituye printf por print
    private final static int ERROR_FOR = 7; //cambiar los ; de for por ,

    /*faltantes, pendientes
    private final static int errorSwitch = 20; //eliminar los break de case
    private final static int errorLibrerias = 8; //eliminar #include del codigo
    private final static int errorScanf = 10; //quita el % de scanf
    private final static int errorTipoDato = 11; //quita el tipo de dato de las declaraciones
    */

    
    private int errores[];
    private int numPermutaciones;
    private int P[]; //permutacion del arreglo errores
    boolean permutacionEncontrada;

    //methods


    /**
     *
     * @param arreglo
     * @param int[], int, int
     */
    private void permuta ( int arreglo[], int  n, int k ) {

       if ( numPermutaciones == k ){
          if ( permutacionEncontrada == false ){
             permutacionEncontrada = true;

             //for (int i = 0; i < 11; i++) // eliminar al final
             //   System.out.print(errores[i] + " "); //
             //System.out.println(""); //

             P = new int[14]; //respaldo de la permutacion
             System.arraycopy(errores, 0, P, 0, 14); //copiar arreglo[] errores al arreglo P[]

          }
          return;
       } //backtracking


       int tmp;

       if (n == 0){
          numPermutaciones++;
       }
       else{
          for (int i = 0; i < n; i++){
             //intercambiar arreglo[i] con arreglo[n-1]
             tmp = arreglo[i];
             arreglo[i] = arreglo[n-1];
             arreglo[n-1] = tmp;
             permuta ( arreglo, n-1, k );
             //invertir el intercambio arreglo[i] con arreglo[n-1]
             tmp = arreglo[i];
             arreglo[i] = arreglo[n-1];
             arreglo[n-1] = tmp;
          } //fin for
       }

    } //fin metodo permuta




    /**
     *
     * @param s
     * @return
     */
    public static ArrayList<String> toStringArray (String s){

       //cuenta total de lineas del codigo C
       int lineas = 0;
        for (int i=0; i < s.length(); i++){
           if (s.charAt(i) == '\n') //contamos saltos de linea
              lineas++;
       }

       //agregamos un salto de linea al final si no existe para evitar confictos
       if (  s.charAt( s.length() - 1  ) != '\n'  ){
          s = s.concat("\n");
          lineas++;
       }

       //inicializar respuesta
       ArrayList<String> result = new ArrayList(); //reservar memoria
     
       for (int i=0; i < lineas; i++)
          result.add( "" ); //inicializar elementos

       int count = 0;
       for (int i = 0; i < s.length(); i++){
          //result[count] = result[count].concat( Character.toString( s.charAt(i) )  );

          result.set( count , result.get(count).concat( Character.toString( s.charAt(i) )  ));

          if ( s.charAt(i) == '\n' )
             count++;
       }//fin for

       //visualizar contenido del arreglo
       //for (int i = 0; i < result.size(); i++)
       //   System.out.print(result.get(i));

       return result;

    } //fin metodo toStringArray





    /**
     *
     * @param s
     * @return
     */
    public static ArrayList<String> toStringArrayNoEspaces (String s){

       //cuenta total de lineas del codigo C
       int lineas = 0;
        for (int i=0; i < s.length(); i++){
           if (s.charAt(i) == '\n') //contamos saltos de linea
              lineas++;
       }

       //agregamos un salto de linea al final si no existe para evitar confictos
       if (  s.charAt( s.length() - 1  ) != '\n'  ){
          s = s.concat("\n");
          lineas++;
       }

       //inicializar respuesta
       ArrayList<String> result = new ArrayList(); //reservar memoria

       for (int i=0; i < lineas; i++)
          result.add( "" ); //inicializar elementos

       int count = 0;
       for (int i = 0; i < s.length(); i++){
          //result[count] = result[count].concat( Character.toString( s.charAt(i) )  );

          result.set( count , result.get(count).concat( Character.toString( s.charAt(i) )  ));

          if ( s.charAt(i) == '\n' )
             count++;
       }//fin for

       //visualizar contenido del arreglo
       //for (int i = 0; i < result.size(); i++)
       //   System.out.print(result.get(i));

       while ( result.indexOf("\n") >= 0 ){
          result.remove(result.indexOf("\n"));
       }
       
       return result;


    } //fin metodo toStringArrayNoEspaces







    /**
     *
     * @param code
     * @param error
     * @return
     */
    public String introduceError(String code, int error){ //cambiar public por private

       
       //variables locales
       String result = "";
       boolean terminado = false;




       /**************************************************************
        * Introduce un error, elimina un punto y coma del codigo fuente
        **************************************************************/
       if ( error == Sintaxis.ERROR_PUNTO_Y_COMA ){

          for (int i=0; i < code.length(); i++){
             if (terminado == false){
                if (  code.charAt(i) == ';' ){
                   terminado = true;
                   result = result.concat( " " ); //cambia el ; por un espacio
                }
                else{
                   result = result.concat( Character.toString( code.charAt(i) )  );
                }
             }
             else{
                result = result.concat( Character.toString( code.charAt(i) )  );
             }
          } //fin for

          return result;

       } //fin else if (error == Sintaxis.ERROR_PUNTO_Y_COMA)




        /***********************************************
        * Introduce un error, cambiando un == por =
        ************************************************/
       else if ( error == Sintaxis.ERROR_OPERADOR_IGUAL){

          for (int i=0; i < code.length() - 1; i++){
             if (terminado == false){
                   if ( code.charAt(i) == '=' && code.charAt(i+1) == '=' ){
                      result = result.concat("=");
                      i++; //para no contar el siguiente =
                      terminado = true;
                   }
                   else{
                      result = result.concat( Character.toString(code.charAt(i)) );
                   }
             }
             else{
                result = result.concat( Character.toString(code.charAt(i)) );
             }
          } //fin for
           
          //finalmente solo concatenamos el ultimo caracter
          result = result.concat ( Character.toString( code.charAt( code.length() - 1 ) ) );

           
          return result;
       } //fin else if(error == Sintaxis.ERROR_OPERADOR_IGUAL)




        /***************************************************************************
        * Introduce un error, cambiando operadores logicos !=, <=, >= por =!, =<, =>
        ***************************************************************************/
       else if (error == Sintaxis.ERROR_OPERADOR_LOGICO){

          for (int i=0; i < code.length() - 1; i++){
             if (terminado == false){
                   if (  (  code.charAt(i) == '!'  || code.charAt(i) == '<' || code.charAt(i) == '>'  )   &&  code.charAt(i+1) == '=' ){
                      //concatenamos los operadores invertidos
                      result = result.concat("=");
                      result = result.concat( Character.toString( code.charAt(i) )  );
                      i++; //para no contar el siguiente que ya se inserto
                      terminado = true;
                   }
                   else{
                      result = result.concat( Character.toString(code.charAt(i)) );
                   }
             }
             else{
                result = result.concat( Character.toString(code.charAt(i)) );
             }

          } //fin for


          //finalmente solo concatenamos el ultimo caracter
          result = result.concat ( Character.toString( code.charAt( code.length() - 1 ) ) );

          return result;
       } //fin else if (error == Sintaxis.ERROR_OPERADOR_LOGICO)



       /************************************************
        * Introduce un error, cambiando main por Main
        ************************************************/
       else if(error == Sintaxis.ERROR_MAIN){

          for (int i = 0; i < code.length(); i++){
             if ( code.indexOf("main") == i ){
                result = result.concat( "M" );
             }
             else{
                result = result.concat(  Character.toString( code.charAt(i) )  );
             }
          }

          return result;

       } //fin else if(error == Sintaxis.ERROR_MAIN)



       /***********************************************
        * Introduce un error, quitando un caracter "
        ***********************************************/
       else if(error == Sintaxis.ERROR_COMILLAS)
       {

          for ( int i = 0; i < code.length() ; i++ ){
             if ( terminado == false ){
                if ( code.charAt(i) == '"' ){
                   result = result.concat(  " " );
                   terminado = true;
                }
                else{
                   result = result.concat( Character.toString( code.charAt(i) ));
                }
             }
             else{
                result = result.concat( Character.toString( code.charAt(i) ));
             }
          } //fin for

          return result;

       } //fin if (error == Sintaxis.ERROR_COMILLAS)


       /**********************************************************************
        * Introduce un error, sustituye printf por print
        **********************************************************************/
       else if ( error == Sintaxis.ERROR_PRINTF ){

       for ( int i = 0; i < code.length(); i++ ){
          if (  i == code.lastIndexOf(  "printf"  ) + 5  ){
             result = result.concat( " " );
          }
          else{
             result = result.concat( Character.toString(  code.charAt(i)  )  );
          }

       } //fin for
          
          return result;
       } // fin else if ( error == Sintaxis.ERROR_PRINTF )






      /*****************************************************
       * Introduce un error, cambiando los ; por , en un for
       *****************************************************/
       else {

          int count = 2;

          for (int i = 0; i < code.length(); i++ ){

             if (  terminado == false  ){
                if ( i == code.indexOf("for")  ){

                   terminado = true;


                   while(count > 0){

                      if ( code.charAt(i) == ';'  ){
                         count --;
                         result = result.concat(",");
                      }
                      else{
                         result = result.concat(  Character.toString( code.charAt(i) ) );
                      }

                      i++;

                   }//fin while
                   
                   i--;

                } //fin if
                else{
                   result = result.concat(  Character.toString( code.charAt(i) ) );
                }
             }
             else{
                result = result.concat(  Character.toString( code.charAt(i) ) );
             }

          } //fin for

          return result;
       } //fin error en for

       



    } //fin metodo

/*********************************
 * fin metodo introduceError
 *********************************/













    /**
     *
     * @param code
     * @return
     *
     */
public ArrayList<String> generaEjercicio ( String code ){

       //ponemos un salto de linea al final del codigo si no existe para evitar inconvenientes
       if ( code.charAt( code.length() - 1 ) != '\n' )
          code = code.concat( "\n" );

       //reservamos memoria para el arreglo de los errores
       errores = new int[14];


       //primera permutacion de los numeros, ordenada
       for (int i = 0; i < 14; i++)
          errores[i] = ( (i+1)  % 7 ) + 1;


       //generar un numero aleatorio k < n!

       Random generador = new Random();

       //encontrar el factorial de 11
       int factK = 1;
       for (int i=2; i <= 12; i++)
          factK *= i;

       
       int k = generador.nextInt( factK )+1;
       k %= 100000;

       //permutar el arreglo k veces
       //System.out.println("Se permutara " + k + " veces"); //comentar

       numPermutaciones = 0;
       permutacionEncontrada = false;


       permuta ( errores, errores.length , k ) ;



       //for (int i=0; i < 11; i++)
       //   System.out.print( P[i] + " ");
       //System.out.print("\n");


       //definir un maximo de errores, considerando el minimo 6, el maximo posible 6
       int totalErrores = generador.nextInt(3) + 3;














       /***************
        *
        * Aqui se generan los errores en el codigo C
        **************/

       for (int i = 0; i < totalErrores; i++){
          //System.out.println("cambio");
          code = introduceError(  code  , P[i]  ); //aplicar el error P[i] al codigo C
          System.out.println(code); //mostrar el codigo para observar su cambio
       }


       //para cada error intentar introducirlo una vez, sino se puede pasar al siguiente
       //cada error puede introducirse una o 2 veces, se introducen a los mas totalErrores

       //devuelve el codigo separado por lineas en un ArrayList<List>
       return toStringArray(code);

    } //fin clase generaEjercicio


public String generaEjercicioString ( String code ){

       //ponemos un salto de linea al final del codigo si no existe para evitar inconvenientes
       if ( code.charAt( code.length() - 1 ) != '\n' )
          code = code.concat( "\n" );

       //reservamos memoria para el arreglo de los errores
       errores = new int[14];


       //primera permutacion de los numeros, ordenada
       for (int i = 0; i < 14; i++)
          errores[i] = ( (i+1)  % 7 ) + 1;


       //generar un numero aleatorio k < n!

       Random generador = new Random();

       //encontrar el factorial de 11
       int factK = 1;
       for (int i=2; i <= 12; i++)
          factK *= i;


       int k = generador.nextInt( factK )+1;
       k %= 100000;

       //permutar el arreglo k veces
       //System.out.println("Se permutara " + k + " veces"); //comentar

       numPermutaciones = 0;
       permutacionEncontrada = false;


       permuta ( errores, errores.length , k ) ;



       //for (int i=0; i < 11; i++)
       //   System.out.print( P[i] + " ");
       //System.out.print("\n");


       //definir un maximo de errores, considerando el minimo 6, el maximo posible 6
       int totalErrores = generador.nextInt(3) + 3;














       /***************
        *
        * Aqui se generan los errores en el codigo C
        **************/

       for (int i = 0; i < totalErrores; i++){
          //System.out.println("cambio");
          code = introduceError(  code  , P[i]  ); //aplicar el error P[i] al codigo C
          System.out.println(code); //mostrar el codigo para observar su cambio
       }


       //para cada error intentar introducirlo una vez, sino se puede pasar al siguiente
       //cada error puede introducirse una o 2 veces, se introducen a los mas totalErrores

       //devuelve el codigo separado por lineas en un ArrayList<List>
       return code;

    } //fin clase generaEjercicio








    /**
     * Incompleto
     * @return
     */
    public boolean revisarSintaxis(String correcta, String s){

      StringTokenizer Stokenizer1 = new StringTokenizer (correcta);
      StringTokenizer Stokenizer2 = new StringTokenizer (s);

      while ( Stokenizer1.hasMoreTokens() && Stokenizer2.hasMoreTokens() ){
         if ( Stokenizer1.nextToken().equals( Stokenizer2.nextToken() ) == false )
            return false;
      }

      if ( Stokenizer1.hasMoreTokens() == false && Stokenizer2.hasMoreTokens() == false   )
         return true;

      return false;
       
    } //fin metodo revisarSintaxis



} //fin clase Sintaxis
